/*
        Packet sniffer using libpcap library
        INSTITUTO POLITECNICO DE BEJA
        MESTRADO EM ENGENHARIA DE SEGURANÇA INFORMATICA
        PARA FINS ACADEMICOS
*/
#include <pcap.h>
#include <stdio.h>
#include <stdlib.h> // for exit()
#include <string.h> //for memset
#include <sys/socket.h>
#include <arpa/inet.h> // for inet_ntoa()
#include <net/ethernet.h>
#include <netinet/udp.h>        //Provides declarations for udp header
#include <netinet/ip.h> //Provides declarations for ip header
#include <netinet/in.h> 
#include <time.h>
#include <sys/types.h>
#include <grp.h>
#include <pwd.h>
#include <unistd.h>
#include <sys/stat.h>
/*  I N C L U D E S  **********************************************************/
#include <libgen.h>
#include <signal.h>
#include <getopt.h>
#include <syslog.h>
#include <fcntl.h>
#include <errno.h>
#include <ctype.h>
#define T_A 1 //Ipv4 address
#define T_NS 2 //Nameserver
#define T_CNAME 5 // canonical name
#define T_SOA 6 /* start of authority zone */
#define T_PTR 12 /* domain name pointer */
#define T_MX 15 //Mail server

//Function Prototypes
//void ngethostbyname (unsigned char* , int);
//void ChangetoDnsNameFormat (unsigned char*,unsigned char*);
//unsigned char* ReadName (unsigned char*,unsigned char*,int*);
//void get_dns_servers();
void process_packet(u_char *, const struct pcap_pkthdr *, const u_char *);
void print_udp_packet(const u_char * , int);
void print_udp_packet_html(const u_char * , int);
void print_udp_packet_csv(const u_char * , int);

void PrintData (const u_char * , int);
u_char* ReadName(unsigned char* reader,unsigned char* buffer,int* count);
void ex_program(int sig);
//void menu();

FILE *logfile;
struct sockaddr_in source,dest;

int dns=0,udp=0, others=0,total=0,i,j;  
int stopApp = 0;
int optionSave = 0;

//DNS header structure
struct DNS_HEADER
{
        unsigned short id; // identification number

        unsigned char rd :1; // recursion desired
        unsigned char tc :1; // truncated message 
        unsigned char aa :1; // authoritive answer
        unsigned char opcode :4; // purpose of message
        unsigned char qr :1; // query/response flag

        unsigned char rcode :4; // response code
        unsigned char cd :1; // checking disabled
        unsigned char ad :1; // authenticated data
        unsigned char z :1; // its z! reserved
        unsigned char ra :1; // recursion available

        unsigned short q_count; // number of question entries
        unsigned short ans_count; // number of answer entries
        unsigned short auth_count; // number of authority entries
        unsigned short add_count; // number of resource entries

};

//Constant sized fields of query structure
struct QUESTION
{
        unsigned short qtype;
        unsigned short qclass;
};

//Constant sized fields of the resource record structure
#pragma pack(push, 1)
struct R_DATA
{
        unsigned short type;
        unsigned short _class;
        unsigned int ttl;
        unsigned short data_len;
};
#pragma pack(pop)

//Pointers to resource record contents

struct RES_RECORD
{
        unsigned char *name;
        struct R_DATA *resource;
        unsigned char *rdata;
};

//Structure of a Query
typedef struct 
{
        unsigned char qname;
        struct QUESTION *ques;
}QUERY;



int main()
{
	//david
	stopApp = 0;
	(void) signal(SIGINT, ex_program);

	optionSave = menu(optionSave);
	if(optionSave == 0)return 0;
	printf("\nmain -> optionSave = %d\n", optionSave);

        pcap_if_t *alldevsp , *device;
        pcap_t *handle; //Handle of the device that shall be sniffed    

        char errbuf[100] , *devname , devs[100][100];
        int count = 1 , n;
        //system("clear");
        //First get the list of available devices
        printf("Finding available devices ... ");
        if( pcap_findalldevs( &alldevsp , errbuf) )
        {
                printf("Error finding devices : %s" , errbuf);
                exit(1);
        }
        printf("Done\n");

        //Print the available devices
        printf("\nAvailable Devices are :\n");
        for(device = alldevsp ; device != NULL ; device = device->next)
        {
                printf("%d. %s - %s\n" , count , device->name , device->description);
                if(device->name != NULL)
                {
                        strcpy(devs[count] , device->name);
                }
                count++;
        }
        
        //Ask user which device to sniff
        printf("Enter the number of the device you want to sniff : ");
        scanf("%d" , &n);
        devname = devs[n];
        
        
        
        //Open the device for sniffing
        printf("Opening device %s for sniffing ... " , devname);
        handle = pcap_open_live(devname , 65536 , 1 , 0 , errbuf);
        
        if (handle == NULL) 
        {
                fprintf(stderr, "Couldn't open device %s : %s\n" , devname , errbuf);
                exit(1);
        }
        printf("Done\n");
        printf("Press Ctrl+C to STOP the capture\n");
       
        logfile=fopen("log.txt","wr");
        if(logfile==NULL) 
        {
                printf("Unable to create file.");
        }
        
        //Put the device in sniff loop
        pcap_loop(handle , -1 , process_packet , NULL);
        return 0;       
}
	  
int menu(int optionSave)
{
    int select = 0;
     	system("clear");
	printf("       * * * * * * * * M.E.N.U.* * * * * * * * *\n");
	printf("       *                                       *\n");
	printf("       *  	Save de capture in:            *\n");
	printf("       *    	    1 -> TXT                   *\n");
	printf("       *    	    2 -> HTML                  *\n");
	printf("       *    	    3 -> CSV - Excel           *\n");
	printf("       *    	    4 -> Exit                  *\n");
	printf("       *                                       *\n");
	printf("       * * * * * * * * * * * * * * * * * * * * *\n");
	printf("\nOption: ");
	if(optionSave == 0)
	{
		scanf(" %d", &select);
		printf("Option choosed: %d\n",select);
	}
	if(optionSave == -1)
	{
		printf(" Invalid option - please select options 1-4: \nOption: ");
		optionSave = 0;
		scanf(" %d", &select);
		printf("Option choosed: %d\n",select);
	}	  
	if (select == 4)
	{
	    	printf("Thanks for using this Sniffer, bye\n");
	    	optionSave = 0;
	    	exit(0);	    	
	}
	  if(select > 4 || select < 0)
	  {
		menu(optionSave -1);
	  }
	
return select;    
}

void process_packet(u_char *args, const struct pcap_pkthdr *header, const u_char *buffer)
{
        int size = header->len;
        
        //Get the IP Header part of this packet , excluding the ethernet header
        struct iphdr *iph = (struct iphdr*)(buffer + sizeof(struct ethhdr));
        ++total;
        
        switch (iph->protocol) //Check the Protocol and do accordingly...
        {
                case 17: //UDP Protocol
                        ++udp;
                        if (optionSave == 1)print_udp_packet(buffer , size);
                        if (optionSave == 2)print_udp_packet_html(buffer , size);
                        if (optionSave == 3)print_udp_packet_csv(buffer , size);
                        break;
                default: //Some Other Protocol like ARP etc.
                        ++others;
                        break;
        }
        
        printf("UDP : %d  || DNS : %d  ||  Others : %d  || Total : %d \r", udp, dns, others, total);
        //pcap_breakloop(handle);
}

void print_ethernet_header(const u_char *Buffer, int Size)
{
        struct ethhdr *eth = (struct ethhdr *)Buffer;
        
        fprintf(logfile , "\n");
        fprintf(logfile , "Ethernet Header\n");
        fprintf(logfile , "   |-Destination Address : %.2X-%.2X-%.2X-%.2X-%.2X-%.2X \n", eth->h_dest[0] , eth->h_dest[1] , eth->h_dest[2] , eth->h_dest[3] , eth->h_dest[4] , eth->h_dest[5] );
        fprintf(logfile , "   |-Source Address      : %.2X-%.2X-%.2X-%.2X-%.2X-%.2X \n", eth->h_source[0] , eth->h_source[1] , eth->h_source[2] , eth->h_source[3] , eth->h_source[4] , eth->h_source[5] );
        fprintf(logfile , "   |-Protocol            : %u \n",(unsigned short)eth->h_proto);
}

void print_ip_header(const u_char * Buffer, int Size)
{
        print_ethernet_header(Buffer , Size);
  
        unsigned short iphdrlen;
                
        struct iphdr *iph = (struct iphdr *)(Buffer  + sizeof(struct ethhdr) );
        iphdrlen =iph->ihl*4;
        
        memset(&source, 0, sizeof(source));
        source.sin_addr.s_addr = iph->saddr;
        
        memset(&dest, 0, sizeof(dest));
        dest.sin_addr.s_addr = iph->daddr;
        
        fprintf(logfile , "\n");
        fprintf(logfile , "IP Header\n");
        fprintf(logfile , "   |-IP Version        : %d\n",(unsigned int)iph->version);
        fprintf(logfile , "   |-IP Header Length  : %d DWORDS or %d Bytes\n",(unsigned int)iph->ihl,((unsigned int)(iph->ihl))*4);
        fprintf(logfile , "   |-Type Of Service   : %d\n",(unsigned int)iph->tos);
        fprintf(logfile , "   |-IP Total Length   : %d  Bytes(Size of Packet)\n",ntohs(iph->tot_len));
        fprintf(logfile , "   |-Identification    : %d\n",ntohs(iph->id));
        fprintf(logfile , "   |-TTL      : %d\n",(unsigned int)iph->ttl);
        fprintf(logfile , "   |-Protocol : %d\n",(unsigned int)iph->protocol);
        fprintf(logfile , "   |-Checksum : %d\n",ntohs(iph->check));
        fprintf(logfile , "   |-Source IP        : %s\n" , inet_ntoa(source.sin_addr) );
        fprintf(logfile , "   |-Destination IP   : %s\n" , inet_ntoa(dest.sin_addr) );
}

void print_udp_packet(const u_char *Buffer , int Size)
{
        
        unsigned short iphdrlen;
        unsigned short udprlen; 
        unsigned char buf[65536],*qname,*reader;                
        struct iphdr *iph = (struct iphdr *)(Buffer +  sizeof(struct ethhdr));
        iphdrlen = iph->ihl*4;
        
        struct udphdr *udph = (struct udphdr*)(Buffer + iphdrlen  + sizeof(struct ethhdr));
        udprlen = ntohs(udph->len);
        
        int header_size =  sizeof(struct ethhdr) + iphdrlen + sizeof udph;
        
        if(ntohs(udph->source)==53 || ntohs(udph->dest)==53){
        fprintf(logfile , "\n\n***********************DNS Packet*************************\n");
        dns++;
        print_ip_header(Buffer,Size);                   
        fprintf(logfile , "\nUDP Header\n");
        fprintf(logfile , "   |-Source Port      : %d\n" , ntohs(udph->source));
        fprintf(logfile , "   |-Destination Port : %d\n" , ntohs(udph->dest));
        fprintf(logfile , "   |-UDP Length       : %d\n" , ntohs(udph->len));
        fprintf(logfile , "   |-UDP Checksum     : %d\n" , ntohs(udph->check));
        fprintf(logfile , "\n");
        fprintf(logfile , "IP Header\n");
        PrintData(Buffer , iphdrlen);
                
        fprintf(logfile , "UDP Header\n");
        PrintData(Buffer+iphdrlen , sizeof udph);
                
        fprintf(logfile , "Data Payload\n");    
        //Move the pointer ahead and reduce the size of string
        //      PrintData(Buffer + header_size , Size - header_size);
        //      struct DNS_HEADER *dnsh = (struct DNS_HEADER*)(Buffer + udprlen  + sizeof(struct ethhdr));      
        struct DNS_HEADER *dnsh = (struct DNS_HEADER*)(Buffer + header_size);
        //fprintf(logfile, "Size DNSH: %d\n", Buffer + header_size);    
        fprintf(logfile , "\nDNS Header\n");
        fprintf(logfile , "   |-ID      : %d\n" , ntohs(dnsh->id));
        fprintf(logfile , "   |-OPCode  : %d\n" , (unsigned int)dnsh->opcode);
        fprintf(logfile , "   |-FlagResp: %d\n" , (unsigned int)dnsh->qr);
        fprintf(logfile , "   |-RCode   : %d\n" , (unsigned int)dnsh->rcode);
        fprintf(logfile , "   |-QCount  : %d\n" , ntohs(dnsh->q_count));
        fprintf(logfile , "   |-ACount  : %d\n" , ntohs(dnsh->ans_count));      
        fprintf(logfile , "   |-RecDes  : %d\n" , (unsigned int)dnsh->rd);
        fprintf(logfile , "   |-RecAva  : %d\n" , (unsigned int)dnsh->ra);
        
        int header_size1 =  sizeof(struct ethhdr) + iphdrlen + udprlen;
        
        struct DNS_HEADER *dns = NULL;  
        struct QUESTION *qinfo = NULL;  
        
        struct sockaddr_in a;   
        
        //move ahead of the dns header and the query field      
        strcpy(buf, Buffer);
        
        fprintf(logfile, "####TESTE##\n");
        PrintData(Buffer + header_size , Size - header_size);
        fprintf(logfile,"####FIM##\n");
        
        dns = (struct DNS_HEADER*)(Buffer + header_size);       
        
        //point to the query portion
        qname =(unsigned char*)(Buffer + header_size + sizeof(struct DNS_HEADER));
        
        int stop=0;
        char *s = ReadName(qname, buf, &stop);
        
        fprintf(logfile , "   |-Name   : %s\n" , s);
        //point to the question portion - type and class
        qinfo =(struct QUESTION*)(Buffer + header_size + sizeof(struct DNS_HEADER)+(strlen((const char*)qname))+2);
        //for reader qinfo =(struct QUESTION*)(Buffer + header_size + sizeof(struct DNS_HEADER)+(strlen((const char*)qname))*2+1);
        fprintf(logfile , "   |-Type   : %d\n" , (unsigned char)qinfo->qtype);  
        fprintf(logfile , "   |-Class  : %d\n" , (unsigned char)qinfo->qclass); 
        //struct QUESTION *qtion = (struct QUESTION*)(Buffer + header_size);
        //fprintf(logfile, "    |HEADER SIZE1: %d\n", header_size);     
        //fprintf(logfile, "    |HEADER SIZE2: %d\n", header_size1);
        //fprintf(logfile, "    |UDPRLEN: %d\n", udprlen);      
        //struct QUESTION *qinfo = (struct QUESTION*)(Buffer + header_size1);
        
        
        //print answers
        if((unsigned int)dnsh->qr == 1){
        
                struct RES_RECORD answers[20], auth[20],addit[20];
                //reader = &buf[sizeof(struct DNS_HEADER) + sizeof((struct QUESTION*)(Buffer + (strlen((const char*)qname)*2+1)+header_size))];
                //reader = &buf[sizeof(struct DNS_HEADER) + sizeof(struct QUESTION) + Buffer + header_size + (strlen((const char*)qname)*2+1)];
                //reader = (unsigned char*)(Buffer + sizeof(struct DNS_HEADER)+ sizeof(struct QUESTION) + (strlen((const char*)qname)*2+1));
                //reader = &buf[sizeof(struct DNS_HEADER) + sizeof(Buffer) +(strlen((const char*)qname)*2) + sizeof(struct QUESTION)];
                //fprintf(logfile, "buf %d\n", sizeof(Buffer) + sizeof(struct DNS_HEADER) + (strlen((const char*)qname)*2) + sizeof(struct QUESTION));
                //not work - reader = &buf[sizeof(struct DNS_HEADER) + (strlen((const char*)qname)+1) + sizeof(struct QUESTION)];
                //fprintf(logfile, "StDNS: %d\n", sizeof(struct DNS_HEADER));
                //fprintf(logfile, "StQUE: %d\n", sizeof(struct QUESTION));
                //fprintf(logfile, "StQUE: %d\n", (strlen((const char*)qname)+1));
                //reader=(unsigned char*)((struct QUESTION*)(Buffer + header_size + sizeof(struct DNS_HEADER)+(strlen((const char*)qname))*2+1)); 
                reader = (unsigned char*)(Buffer + header_size +sizeof(struct DNS_HEADER) + sizeof(struct QUESTION))-8;
                /////////////////////////////////////////////////////////////////////////////
                ////////////////////
                //Start reading answers
                int stop1=0;

                for(i=0;i<ntohs(dns->ans_count);i++)
                {
                        answers[i].name=ReadName(reader,buf,&stop1);
                        fprintf(logfile, "ReadName: %s", ReadName(reader,buf,&stop1));
                        reader = reader + stop1;

                        answers[i].resource = (struct R_DATA*)(reader);
                        reader = reader + sizeof(struct R_DATA);

                        if(ntohs(answers[i].resource->type) == 1) //if its an ipv4 address
                        {
                                answers[i].rdata = (unsigned char*)malloc(ntohs(answers[i].resource->data_len));

                                for(j=0 ; j<ntohs(answers[i].resource->data_len) ; j++)
                                {
                                        answers[i].rdata[j]=reader[j];
                                }

                                answers[i].rdata[ntohs(answers[i].resource->data_len)] = '\0';

                                reader = reader + ntohs(answers[i].resource->data_len);
                        }
                        else
                        {
                                answers[i].rdata = ReadName(reader,buf,&stop1);
                                reader = reader + stop1;
                        }
                }
                ////////////////////////////
                /////////////////////////////////////////////////////////////////////////////
                if(ntohs(dns->ans_count)>0){    
                        fprintf(logfile, "    |Answer Records : %d \n" , ntohs(dns->ans_count) );       
                        for(i=0 ; i < ntohs(dns->ans_count) ; i++)
                        {
                        

                        if( ntohs(answers[i].resource->type) == T_A) //IPv4 address
                        {
                                long *p;
                                p=(long*)answers[i].rdata;
                                a.sin_addr.s_addr=(*p); //working without ntohl
                                fprintf(logfile, "Name : %s has IPv4 address : %s", answers[i].name ,inet_ntoa(a.sin_addr));
                        }
                        
                        if(ntohs(answers[i].resource->type)==5) {
                                //Canonical name for an alias
                                fprintf(logfile, "IP : %s has alias name : %s",answers[i].name, answers[i].rdata);
                        }
        
                        fprintf(logfile,"\n");
                        }
                }
                fprintf(logfile , "\n###########################################################");
    }
}
	
	if(stopApp==1)
	 exit(0);
}


//david 2
void ex_program(int sig) 
{
	 printf("\nCapture terminated... !!\n");
	 (void) signal(SIGINT, SIG_DFL);
	 
	 stopApp = 1;
}


void PrintData (const u_char * data , int Size)
{ 

        int i , j;
        for(i=0 ; i < Size ; i++)
        {
                if( i!=0 && i%16==0)   //if one line of hex printing is complete...
                {
                        fprintf(logfile , "         ");
                        for(j=i-16 ; j<i ; j++)
                        {
                                if(data[j]>=32 && data[j]<=128)
                                        fprintf(logfile , "%c",(unsigned char)data[j]); //if its a number or alphabet
                                
                                else fprintf(logfile , "."); //otherwise print a dot
                        }
                        fprintf(logfile , "\n");

                } 
                
                if(i%16==0) fprintf(logfile , "   ");
                        fprintf(logfile , " %02X",(unsigned int)data[i]);
                                
                if( i==Size-1)  //print the last spaces
                {
                        for(j=0;j<15-i%16;j++) 
                        {
                          fprintf(logfile , "   "); //extra spaces
                        }
                        
                        fprintf(logfile , "         ");
                        
                        for(j=i-i%16 ; j<=i ; j++)
                        {
                                if(data[j]>=32 && data[j]<=128) 
                                {
                                  fprintf(logfile , "%c",(unsigned char)data[j]);
                                }
                                else 
                                {
                                  fprintf(logfile , ".");
                                }
                        }
                        
                        fprintf(logfile ,  "\n" );
                }
        }
        
   
}

void print_udp_packet_html(const u_char *Buffer , int Size)
{}

void print_udp_packet_csv(const u_char *Buffer , int Size)
{}

u_char* ReadName(unsigned char* reader, unsigned char* buffer, int* count)
{
        unsigned char *name;
        unsigned int p=0,jumped=0,offset;
        int i , j;

        *count = 1;
        name = (unsigned char*)malloc(256);

        name[0]='\0';

        //read the names in 3www6google3com format
        while(*reader!=0)
        {
                if(*reader>=192)
                {
                        offset = (*reader)*256 + *(reader+1) - 49152; //49152 = 11000000 00000000 ;)
                        reader = buffer + offset - 1;
                        jumped = 1; //we have jumped to another location so counting wont go up!
                }
                else
                {
                        name[p++]=*reader;
                }

                reader = reader+1;

                if(jumped==0)
                {
                        *count = *count + 1; //if we havent jumped to another location then we can count up
                }
        }

        name[p]='\0'; //string complete
        if(jumped==1)
        {
                *count = *count + 1; //number of steps we actually moved forward in the packet
        }

        //now convert 3www6google3com0 to www.google.com
        for(i=0;i<(int)strlen((const char*)name);i++) 
        {
                p=name[i];
                for(j=0;j<(int)p;j++) 
                {
                        name[i]=name[i+1];
                        i=i+1;
                }
                name[i]='.';
        }
        name[i-1]='\0'; //remove the last dot
        return name;
}

